import logging
from httplib import HTTPMessage
from cStringIO import StringIO


class HTTPMixin(object):
    def get_http(self, sock, body_size_limit=0):
        self.http_dict = {'body': ""}
        for y in self.readall(sock, seps=("\n\r\n", "\n\n")):
            yield y
        sl = len(self.sep)
        #print >> sys.stderr, self.sep, self.rdata
        i = self.rdata.index(self.sep)
        hs = self.rdata[:i + 1]
        dl = self.rdata[i + sl:]
        shs = StringIO(hs)

        stl = shs.readline()
        try:
            v, s, r = stl.split(None, 2)
            if not v.startswith("HTTP"):
                raise Exception("Miss status line")
            self.http_dict['version'] = v
            self.http_dict['status'] = s
            self.http_dict['reason'] = r
        except Exception, e:
            raise Exception("Miss status line %s" % e)

        h = HTTPMessage(shs, 0)
        self.http_dict['headers'] = h
        self.http_dict['header_string'] = hs

        # http://en.wikipedia.org/wiki/Chunked_transfer_encoding
        c = h.getheader("Transfer-Encoding", "").lower()
        if c == "chunked":
            for y in self._get_chunked(sock, dl):
                yield y
            return
        c = h.getheader("Content-Length", None)
        # c == None means we wait for socket close, or get body_size_limit
        if c != None:
            ls = cl = int(c)
            # for rate result, don't want to trans whole unrated web page
            if body_size_limit:
                cl = min(body_size_limit, cl)
        else:
            ls, cl = None, body_size_limit
        ds = max(cl - len(dl), 0)
        # get all data
        # c: content length from header, N, 0, 1
        # b: body limit, 0, 1
        # d: downloaded body length, < not enough, > enough
        #|----------------------
        #| c, b, d  |          |
        #|----------------------
        #| N, 0, x  | to close |
        #| N, 1, <  | to b     |
        #| N, 1, >  | skip     |
        #| 0, x, x  | skip     |
        #| 1, 0, <  | to c     |
        #| 1, 0, >  | skip     |
        #| 1, 1, <  | to b     |
        #| 1, 1, >  | skip     |
        #|---------------------|
        if ds or (c == None and body_size_limit == 0):
            try:
                for y in self.readall(sock, size=ds):
                    yield y
            except SocketClosed:
                if c != None:
                    raise   # we not wait for socket close, raise it
            dl += self.rdata
        if ls != cl:
            # we cut the data by body_size_limit
            h["Content-Length"] = str(len(dl))
            hs = stl + "".join(h.headers)
        self.http_dict['body'] = dl

    # need more testing
    # http://en.wikipedia.org/wiki/Chunked_transfer_encoding
    def _get_chunked(self, sock, data):
        body = ""
        dl = data
        while True:
            # get chunk size
            #print "dl =", repr(dl)
            i = dl.find("\n")
            if "\n" not in dl:
                for y in self.readall(sock, seps=("\n",)):
                    yield y
                dl += self.rdata
            #print "dl =", repr(dl)
            i = dl.index("\n")
            l = int(dl[:i + 1], 16)
            #print "l =", l
            if l == 0:
                break
            dl = dl[i + 1:]
            # get data
            if len(dl) < l:
                for y in self.readall(sock, size=(l - len(dl))):
                    yield y
                dl += self.rdata
            #print "data =", repr(dl)
            body += dl[:l]
            dl = dl[l:]
            # data ending
            if "\n" not in dl:
                for y in self.readall(sock, seps=("\n",)):
                    yield y
                dl += self.rdata
            i = dl.find("\n")
            dl = dl[i + 1:]
        self.http_dict['body'] = body

    def build_http_msg(self, status, msg, headers={}):
        ret = StringIO()
        ret.write("%s\r\n" % status)
        #logging.debug("headers = %s" % str(headers))
        for h, v in headers.iteritems():
            ret.write("%s: %s\r\n" % (h, v))

        if "Content-Type" not in headers and msg:
            ret.write("Content-Type: text/html; charset=UTF-8\r\n")
        if "Content-Length" not in headers and msg:
            ret.write("Content-Length: %d\r\n" % len(msg))

        ret.write("\r\n")
        ret.write(msg)
        #logging.debug(ret.getvalue())
        return ret.getvalue()

    def response_200(self, msg, headers={}):
        return self.build_http_msg("HTTP/1.0 200 OK", msg, headers)

    def response_400(self, msg, headers={}):
        return self.build_http_msg("HTTP/1.0 400 Bad Request", msg, headers)

    def request_get(self, path, headers={}, host=None):
        if host:
            get = "GET %s HTTP/1.1" % path
            headers["Host"] = host
        else:
            get = "GET %s HTTP/1.0" % path
        return self.build_http_msg(get, "", headers)
